# -*- coding: utf-8 -*-

import abc
import atexit
import logging
import logging.config
import os
import signal
import sys
import time
from argparse import ArgumentParser

import util
from component import Component

reload(sys)
sys.setdefaultencoding('utf8')


def signal_handler(signal_num, frame):
    print(' Bye-bye.')
    sys.exit(signal_num)

signal.signal(signal.SIGINT, signal_handler)


@atexit.register
def cleanup():
    logging.shutdown()


class Worker(object):

    def __init__(self):
        self.__work_path = os.getcwd()
        self.__conf_path = '%s/conf' % self.__work_path
        self.__paths = {}

        self.__parse_args()

        self.__data_path = '%s/data/%s' % (self.__work_path, self.__task_full_name)
        self.__log_path = '%s/log' % self.__data_path

        self.__logger_name = 'data'

        self._cfg = {}

        self.__load_cfg()

        self._logger = logging.getLogger(self.__logger_name)

        self._components = {}
        if 'components' in self._cfg and type(self._cfg['components']) == list:
            for x in self._cfg['components']:
                self._components[x['name']] = Component.create(x['class_name'], {
                    'worker': self,
                    'cfg': x['cfg']
                })

        self._init()

        self._logger.info('Start ... [%s] %s' % (self.__task_full_name, self._cfg))

    def __del__(self):
        self.end()

    @property
    def logger(self):
        return self._logger

    @property
    def work_path(self):
        return self.__work_path

    @property
    def task_name(self):
        return self.__task_name

    @property
    def task_full_name(self):
        return self.__task_full_name

    @property
    def env_name(self):
        return self.__env_name

    @property
    def env_path(self):
        return self.__env_path

    @abc.abstractmethod
    def run(self, params=None):
        raise NotImplementedError('call to abstract method %s.run' % self.__class__)

    def path(self, name):
        p = self.__paths.get(name, '')
        if not p:
            p = '%s/%s' % (self.__data_path, name)
            util.make_dirs(p)
            self.__paths[name] = p

        return p

    def sleep(self, sleep_time=None):
        if not isinstance(sleep_time, int) or sleep_time <= 0:
            sleep_time = self._cfg['sleep_time'] if 'sleep_time' in self._cfg else 18

        self._logger.info('Sleeping ... (%ds)' % sleep_time)

        time.sleep(sleep_time)

    def end(self):
        try:
            self._end()

            for x in self._components.values():
                x.destroy()

            self._cfg = None

            self._logger.info('End ...')
        except Exception as e:
            if self._logger:
                self._logger.warning('%s:end(): %s\n' % (self.__class__.__name__, e))

    def _end(self):
        pass

    def _init(self):
        pass

    def _get_component(self, name, attrs=[]):
        com = self._components.get(name, None)
        if not com:
            raise Exception('Unconfigured component: %s' % name)

        if attrs:
            for x in attrs:
                if not hasattr(com, x):
                    raise Exception('Missing attribute: %s.%s' % (com, x))

        return com

    def __load_cfg(self):
        sys.path.append(self.__env_path)

        cfg_module = __import__(self.__task_name)

        if hasattr(cfg_module, 'DATA_PATH') and cfg_module.DATA_PATH:
            self.__data_path = '%s/%s/%s' % (self.__work_path, cfg_module.DATA_PATH, self.__task_full_name)

        if hasattr(cfg_module, 'PREPARE_FOR_DIRS') and cfg_module.PREPARE_FOR_DIRS:
            for x in cfg_module.PREPARE_FOR_DIRS:
                p = ''
                if x == 'log':
                    p = self.__log_path
                elif x == 'cache':
                    p = '%s/cache' % self.__data_path
                    self.__cache_path = p
                elif x == 'state':
                    p = '%s/state' % self.__data_path
                    self.__state_path = p

                if p:
                    self.__paths[x] = p
                    util.make_dirs(p)

        fn = '%s/%s' % (self.__env_path, cfg_module.LOGGING_CONF if hasattr(cfg_module, 'LOGGING_CONF') else 'logging.conf')
        if os.path.isfile(fn):
            try:
                logging.config.dictConfig(eval(util.load_file(fn).replace('@log_path', self.__log_path)))
            except Exception as e:
                print('Logging initialize error: [%s] %s' % (fn, e))
                sys.exit(1)

        if hasattr(cfg_module, 'LOGGER_NAME') and cfg_module.LOGGER_NAME:
            self.__logger_name = cfg_module.LOGGER_NAME

        if hasattr(cfg_module, 'CFG') and cfg_module.CFG:
            self._cfg = cfg_module.CFG

    def __parse_args(self):
        argp = ArgumentParser()
        argp.add_argument('task_name')
        argp.add_argument('-e', dest='env_name', required=True)
        args = argp.parse_args()

        self.__task_name = args.task_name
        self.__env_name = args.env_name
        self.__task_full_name = '%s-%s' % (self.__env_name, self.__task_name)
        self.__env_path = '%s/%s' % (self.__conf_path, self.__env_name)
        self.__task_cfg_filename = '%s/%s.py' % (self.__env_path, self.__task_name)

        if not os.path.isfile(self.__task_cfg_filename):
            print('No such file: %s' % self.__task_cfg_filename)
            sys.exit(1)
